package com.push_label.config;


import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class RedisConfig {

/**
 * RedisTemplate 配置类
 * 用于自定义 Redis 操作模板的序列化方式
 */
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    // 创建 RedisTemplate 实例，指定 key 为 String 类型，value 为 Object 类型
    RedisTemplate<String, Object> template = new RedisTemplate<>();
    // 设置 Redis 连接工厂
    template.setConnectionFactory(factory);

    // 创建字符串序列化器，用于 key 和 hash key 的序列化
    // StringRedisSerializer 是 Spring 提供的字符串序列化器，保证 key 的可读性
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    
    // 创建 JSON 序列化器，用于 value 和 hash value 的序列化
    // Jackson2JsonRedisSerializer 可以将对象序列化为 JSON 格式存储到 Redis
    Jackson2JsonRedisSerializer<Object> jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
    
    // 创建并配置 ObjectMapper
    ObjectMapper objectMapper = new ObjectMapper();
    
    // 定义日期时间格式
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    
    // 创建 JavaTimeModule 用于处理 Java 8 时间 API
    JavaTimeModule timeModule = new JavaTimeModule();
    // 注册 LocalDateTime 的反序列化器
    timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
    // 注册 LocalDateTime 的序列化器
    timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));

    // 将时间模块注册到 ObjectMapper
    objectMapper.registerModule(timeModule);
    
    // 设置字段可见性：所有字段（包括私有字段）都可以被序列化
    objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    
    // 激活默认类型信息，用于支持多态类型的序列化和反序列化
    // DefaultTyping.NON_FINAL: 对所有非 final 类型添加类型信息
    objectMapper.activateDefaultTyping(
        objectMapper.getPolymorphicTypeValidator(), 
        ObjectMapper.DefaultTyping.NON_FINAL
    );
    
    // 将配置好的 ObjectMapper 设置到 JSON 序列化器中
    jsonRedisSerializer.setObjectMapper(objectMapper);

    // 设置 RedisTemplate 的序列化器
    
    // 设置 value 的序列化器：使用 JSON 序列化器
    template.setValueSerializer(jsonRedisSerializer);
    
    // 设置 key 的序列化器：使用字符串序列化器（保证 key 的可读性）
    template.setKeySerializer(stringRedisSerializer);
    
    // 设置 hash key 的序列化器：使用字符串序列化器
    template.setHashKeySerializer(stringRedisSerializer);
    
    // 设置 hash value 的序列化器：使用 JSON 序列化器
    template.setHashValueSerializer(jsonRedisSerializer);

    // 初始化模板，确保所有属性都已设置
    template.afterPropertiesSet();
    
    return template;
}


    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);

        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter));
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter));
        mapper.registerModule(timeModule);
        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

        GenericJackson2JsonRedisSerializer jackson = new GenericJackson2JsonRedisSerializer(mapper);

        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(60))
                .disableCachingNullValues()
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(
                                new StringRedisSerializer()
                        )
                )
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(jackson)
                );

        Map<String, RedisCacheConfiguration> configs = new HashMap<>();
        configs.put("previewLabel", config.entryTtl(Duration.ofHours(3 * 24 - 1)));

        return RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .transactionAware()
                .withInitialCacheConfigurations(configs)
                .build();
    }


    @Bean
    public RedissonClient redisClient() throws IOException {
        String redissonConfig = System.getProperty("redisson.config");
        Config config = null;

        try {
            if (redissonConfig != null) {
                try {
                    FileInputStream fis = new FileInputStream(redissonConfig);
                    config = Config.fromYAML(fis);
                } catch (FileNotFoundException e) {
                    throw new RuntimeException(e);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        if (config == null) {
            try (InputStream is = getClass().getClassLoader().getResourceAsStream("redisson.yaml")) {
                if (is == null) {
                    throw new RuntimeException("redisson.yaml not found");
                }
                config = Config.fromYAML(is);

            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return  Redisson.create(config);
    }

}
